class Compile{
    constructor(el,vm){
       this.el=this.isElementNode(el)?el:document.querySelector(el);
      
       this.vm=vm;
      if(this.el){
          // 如果这个元素能获取到， 我们就开始编译
          // 1. 先把这些真实的dom  ， 移动到内存中 fragment
           let fragment= this.node2Fragment(this.el)
          //  return
          // 2.编译 =》 提取 元素节点 v-model  和文本节点 {{}}
           this.compile(fragment)
          // 3. 把编译好的fragment 在赛回到页面中去

          this.el.appendChild(fragment);
      }
       
    }
    /* 专门写一些辅助方法 */
    isElementNode(node){
        return node.nodeType === 1;

    }
    isEventDirective(name) {
        return name.includes('on:');
    }

    isDirective(name){
      return name.includes('v-')
    }
    /* 核心的方法 */
    compileElement(node){
       // 带 v-model 
      let attrs= node.attributes;  // 取出当前节点的属性
       Array.from(attrs).forEach((attr)=>{
        //    console.log(attr.name)
           let attrName = attr.name
           if(this.isDirective(attrName)){
                 // 取到对应的值   放到节点
                let expr = attr.value;
                let [,type] = attrName.split('-')
                 if(this.isEventDirective(type)){ // 事件指令
                Compileutil.Event(node,this.vm,expr, type)
                 } else {
                   // node this.vm.$data  expr  
                 Compileutil[type](node,this.vm,expr)
                 //tode .................
                 }
                 
           }
       })
    }
    compileText(node){
      // 带 {{}}
      let expr = node.textContent;
      let reg = /\{\{([^}]+)\}\}/g;
      if(reg.test(expr)){
         //node this.vm.$data  expr
         Compileutil['text'](node,this.vm,expr)
         // tode ...........
      }
    }

    compile(fragment){
        // 需要 递归  
      let childNodes = fragment.childNodes;
      Array.from(childNodes).forEach((node)=>{
        if(this.isElementNode(node)){
           // 是元素节点
           // 如果是元素节点 还需要继续
            this.compileElement(node)
            this.compile(node);
            //这里需要编译元素
        }else{
            // 文本节点
            //这里需要编译元素
            this.compileText(node)
        }
      })
    }

    node2Fragment(el){ // 需要将el 中的内容全部放在内存中去
        // 文档碎片 内存中的 dom 节点
     let fragment = document.createDocumentFragment();
     let firstchild;
    //   Array.from(el.childNodes).forEach((node)=>{
    //     console.log(node)
    //   fragment.appendChild(node)
    //  })
    //   return fragment

     while(firstchild = el.firstChild){
        fragment.appendChild(firstchild)
     }
    //  fragment.appendChild(el.childNodes)
     return fragment ; // 内存中的节点
    }
}


Compileutil= {
    getVal(vm,expr){
        expr= expr.split('.');
        //获取实例上对应的数据
        return expr.reduce((perv,next,index)=>{ // vm.$data.a
           return perv[next]
        },vm.$data);
    },
    setValue(vm,expr,value){
      expr= expr.split('.');
      //获取实例上对应的数据
      return expr.reduce((perv,next,index)=>{ // vm.$data.a
          if(index === expr.length-1){
             return perv[next] = value
          }
         return perv[next]
      },vm.$data);
    },
    getTextVal(vm,expr){  // 获取编译文本后的结果
      return  expr.replace(/\{\{([^}]+)\}\}/g,(...arguments)=>{ 
                return this.getVal(vm,arguments[1])
         })
    },
    text(node,vm,expr){ // 文本处理
    let upttateFn = this.updater['TextUpdater'];
      let value= this.getTextVal(vm,expr)
      expr.replace(/\{\{([^}]+)\}\}/g,(...arguments)=>{ 
        new Watchwer(vm,arguments[1],(newValue)=>{
            //  如果数据变化了 文本节点会重新获取依赖的属性跟新文本的内容
            upttateFn&&upttateFn(node,this.getTextVal(vm,expr))
            // upttateFn&&upttateFn(node,newValue)
        })
      })
      upttateFn&&upttateFn(node,value)

    },
    model(node,vm,expr){ // 输入框的处理
        let upttateFn = this.updater['modelUpdater'];
        //这里应该加一个监控  数据变化l 加一个Watchwer
        new Watchwer(vm,expr,(newValue)=>{
            // 值变化后 会将新值传过来
            // upttateFn&&upttateFn(node,this.getVal(vm,expr))
            upttateFn&&upttateFn(node,newValue)
        })
        node.addEventListener('input',(e)=>{
            let newValue = e.target.value;
            this.setValue(vm,expr,newValue)
        })
        upttateFn&&upttateFn(node,this.getVal(vm,expr))
    },
    html(node,vm,expr){ // 输入框的处理
      let upttateFn = this.updater['htmlUpdater'];
      //这里应该加一个监控  数据变化l 加一个Watchwer
      new Watchwer(vm,expr,(newValue)=>{
          // 值变化后 会将新值传过来
          // upttateFn&&upttateFn(node,this.getVal(vm,expr))
          upttateFn&&upttateFn(node,newValue)
      })
      upttateFn&&upttateFn(node,this.getVal(vm,expr))
    },
    Event(node, vm, expr, type) {
      const [,eventType] = type.split(':');
      console.log(vm)

      const cb = vm.methods && vm.methods[expr];
      if (eventType && cb) {
          node.addEventListener(eventType, cb.bind(vm), false);
        }
    },
    updater:{
        // 文本更新
        TextUpdater(node,value){
           node.textContent =value
        },
        // 输入框更新
        modelUpdater(node,value){
            node.value=value
        },
        htmlUpdater(node,value){
          node.innerHTML = value
        }
    }

}